package utils

import (
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"crypto/md5"
	"crypto/tls"
	"encoding/hex"
	"gozero-microservices/gateway-api/define"
	"html/template"
	"io"
	"math/rand"
	"net/http"
	"net/smtp"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/jordan-wright/email"
	"golang.org/x/crypto/bcrypt"
)

/*
 +----------------------------------------------------------------------
 + Title        : 助手函数
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2021-09-22
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : 助手函数
 +----------------------------------------------------------------------
*/

//时间戳转换日期
func UnixToDate(timestamp int) string {
	return time.Unix(int64(timestamp), 0).Format("2006-01-02 15:04:05")
}

//日期转换时间戳
func DateToUnix(date string) int64 {
	t, err := time.ParseInLocation("2006-01-02 15:04:05", date, time.Local)
	if err != nil {
		return 0
	}

	return t.Unix()
}

//获取时间戳
func GetUnix() int64 {
	return time.Now().Unix()
}

//获取纳秒数
func GetUnixNano() int64 {
	return time.Now().UnixNano()
}

//获取当前日期
func GetDate() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

//获取当前年-月-日
func GetDay() string {
	return time.Now().Format("2006-01-02")
}

/**
 * md5加密
 *
 * @param string value -----------------------------必传,值
 * @return string
 * @author huwl
 */
func Md5Str(value string) string {
	h := md5.New()
	io.WriteString(h, value)
	return hex.EncodeToString(h.Sum(nil))
}

/**
 * bcrypt加密
 *
 * @param string value -----------------------------必传,值
 * @return string error
 * @author huwl
 */
func Bcrypt(value string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(value), bcrypt.DefaultCost)
	return string(hash), err
}

/**
 * bcrypt加密对比
 *
 * @param string hashValue -------------------------必传,加密值
 * @param string value -----------------------------必传,明文值
 * @return bool
 * @author huwl
 */
func CompareBcrypt(hashValue, value string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hashValue), []byte(value))
	return err == nil
}

/**
 * 获取应用路径
 *
 * @return string
 * @author huwl
 */
func GetAppPath() string {
	file, _ := exec.LookPath(os.Args[0])
	path, _ := filepath.Abs(file)
	index := strings.LastIndex(path, string(os.PathSeparator))

	return path[:index]
}

/**
 * 三目运算的函数
 *
 * @param bool a -----------------------------------必传
 * @param interface{} b ----------------------------必传
 * @param interface{} c ----------------------------必传
 * @return interface{}
 * @author huwl
 */
func Ternary(a bool, b, c interface{}) interface{} {
	if a {
		return b
	}
	return c
}

/**
 * 分割时间2022-08-23T22:56:34+08:00 ----> 2022-08-23 22:56:34
 *
 * @param time.Time valueStr -----------------------必传
 * @return string
 * @author huwl
 */
func CutTime(valueStr time.Time) string {
	return valueStr.Format("2006-01-02 15:04:05")
}

/**
 * 是否包含字符串
 *
 * @param []string items ---------------------------必传
 * @param string item ------------------------------必传
 * @return bool
 * @author huwl
 */
func IsContainStr(items []string, item string) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

/**
 * 是否包含指定的数字
 *
 * @param []T items -------------------------必传
 * @param T item ---------------------------必传
 * @return bool
 * @author huwl
 */
func IsContainNumber[T uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64 | uint | int](items []T, item T) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

/**
 * 将 [](uint8|int8|uint16|int16|uint32|int32|uint64|int64) 转换为 []int
 *
 * @param []T src ---------------------------必传
 * @return []int
 * @author huwl
 */
func ConvertToInt[T uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64](src []T) []int {
	dst := make([]int, len(src))
	for i, val := range src {
		dst[i] = int(val)
	}
	return dst
}

/**
 * 处理两位小数点
 *
 * @param interface{} value ------------------------必传
 * @return string
 * @author huwl
 */
func HandleTwoDecimalPoint(value interface{}) string {
	val, ok := value.(string)

	money := ""
	if ok {
		i, _ := strconv.Atoi(val)
		money = strconv.FormatFloat(float64(i)/100, 'f', 2, 64)
	}

	i, ok := value.(int)
	if ok {
		money = strconv.FormatFloat(float64(i)/100, 'f', 2, 64)
	}

	return money
}

/**
 * 运算
 *
 * @param interface{} value ------------------------必传
 * @return string
 * @author huwl
 */
func Operation[T uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64 | uint | int](n T, operator string, m T) T {

	switch {
	case operator == "+":
		return n + m
	case operator == "-":
		return n - m
	case operator == "*":
		return n * m
	case operator == "/":
		return n / m
	}

	return 0
}

/**
 * 拆分字符串
 *
 * @param string value -----------------------------必传
 * @param string sep -------------------------------必传
 * @return []string
 * @author huwl
 */
func StringSplit(value, sep string) []string {
	return strings.Split(value, sep)
}

/**
 * 字符串转换成html
 *
 * @param string value -----------------------------必传
 * @return template.HTML
 * @author huwl
 */
func StrToHtml(value string) template.HTML {
	return template.HTML(value)
}

/**
 * 错误输出
 *
 * @param int code ---------------------------------必传
 * @param string message ---------------------------必传
 * @return map[string]interface{}
 * @author huwl
 */
func ErrorOutput(message string, code ...int) map[string]interface{} {
	statusCode := 400
	if len(code) > 0 {
		statusCode = code[1]
	}

	return map[string]interface{}{
		"code":    statusCode,
		"data":    nil,
		"message": message,
	}
}

/**
 * 成功输出
 *
 * @param int code ---------------------------------必传
 * @param interface{} data -------------------------必传
 * @return map[string]interface{}
 * @author huwl
 */
func SuccessOutput(data interface{}, code ...int) map[string]interface{} {
	statusCode := 200
	if len(code) > 0 {
		statusCode = code[1]
	}

	return map[string]interface{}{
		"code":    statusCode,
		"data":    data,
		"message": "",
	}
}

/**
 * 生成验证码
 *
 * @return int
 * @author huwl
 */
func GenerateCode() int {
	rand.Seed(time.Now().UnixNano())
	code := rand.Intn(9999)
	if code < 1000 {
		code += 1000
	}
	return code
}

//生成随机数
func GenerateRandomNumber(length int) string {
	number := ""
	for i := 0; i < length; i++ {
		number += strconv.Itoa(rand.Intn(10))
	}

	return number
}

/**
 * 验证手机号
 *
 * @param string mobile ----------------------------必传
 * @return bool
 * @author huwl
 */
func CheckMobile(mobile string) bool {
	if mobile == "" {
		return false
	}
	pattern := `^1[3|5|7|8|9][\d]{9}$`
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(mobile)
}

//Des加密
//origData=[]byte("this is string")
//key=[]byte("12345678")
func DesEncrypt(origData, key []byte) ([]byte, error) {
	block, err := des.NewCipher(key)
	if err != nil {
		return nil, err
	}

	origData = PKCS5Padding(origData, block.BlockSize())
	bm := cipher.NewCBCEncrypter(block, key)
	crypted := make([]byte, len(origData))
	bm.CryptBlocks(crypted, origData)
	return crypted, nil
}

//Des解密
//crypted=[]byte("加密串")
//key=[]byte("12345678")
func DesDecrypt(crypted, key []byte) ([]byte, error) {
	block, err := des.NewCipher(key)
	if err != nil {
		return nil, err
	}

	bm := cipher.NewCBCDecrypter(block, key)
	origData := make([]byte, len(crypted))
	bm.CryptBlocks(origData, crypted)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

//
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	b := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, b...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	i := int(origData[length-1])
	return origData[:(length - i)]
}

/**
 * 分割数字
 *
 * @param string str -------------------------------必传
 * @param strin delimiter --------------------------必传
 * @return []int
 * @author huwl
 */
func IntSplit(str string, delimiter string) []int {
	contents := strings.Split(str, delimiter)
	numbers := []int{}
	for _, content := range contents {
		i, _ := strconv.Atoi(content)
		numbers = append(numbers, i)
	}

	return numbers
}

/**
 * 发送邮件
 *
 * @param string from ------------------------------必传
 * @param strin to ---------------------------------必传
 * @param strin subject ----------------------------必传
 * @param strin contentType ------------------------必传
 * @param strin content ----------------------------必传
 * @return error
 * @author huwl
 */
func SendEmail(from string, to []string, subject string, contentType, content string) error {
	e := email.NewEmail()
	e.From = from
	e.To = to
	e.Subject = subject
	if contentType == "text" {
		e.Text = []byte(content)
	} else {
		e.HTML = []byte(content)
	}

	return e.SendWithTLS(define.EmailAddr+":"+define.EmailPort, smtp.PlainAuth("", define.EmailUsername, define.EmailPassword, define.EmailAddr),
		&tls.Config{
			InsecureSkipVerify: true,
			ServerName:         define.EmailAddr,
		})
}

/**
 * 生成二维码
 *
 * @param *gin.Context ctx -----------------------------------必传
 * @param string contents ------------------------------------必传
 * @param string mode ----------------------------------------必传
 * @return string, error
 * @author huwl
 */
// func GenerateQrcode(ctx *gin.Context, contents, mode string) (string, error) {
// 	if mode == "image" {
// 		cfg, err := GetConfig("app", "yml")
// 		if err != nil {
// 			return "", errors.New("读取APP配置文件")
// 		}

// 		savePath := cfg.GetString("dev.qrcode_save_path")
// 		err = qrcode.WriteFile(contents, qrcode.Medium, 256, savePath)
// 		if err != nil {
// 			return "", err
// 		}

// 		b, err := ioutil.ReadFile(savePath)
// 		if err != nil {
// 			return "", err
// 		}

// 		return string(b), nil
// 	}

// 	var png []byte
// 	png, err := qrcode.Encode(contents, qrcode.Medium, 256)
// 	if err != nil {
// 		return "", err
// 	}

// 	return string(png), nil
// }

// /**
//  * dchest版生成验证码
//  *
//  * @param *gin.Context ctx -----------------------------------必传
//  * @param []int length ---------------------------------------必传
//  * @return error
//  * @author huwl
//  */
// func GenerateCaptcha(ctx *gin.Context, length ...int) error {
// 	u := captcha.DefaultLen
// 	width, height := 107, 36

// 	switch len(length) {
// 	case 1:
// 		u = length[0]
// 	case 2:
// 		u = length[1]
// 	case 3:
// 		u = length[2]
// 	}

// 	captchaId := captcha.NewLen(u)
// 	session := sessions.Default(ctx)
// 	session.Set("captcha", captchaId)
// 	session.Save()
// 	// captcha.Server(ctx.Writer, ctx.Request, captchaId, ".png", "zh", false, w, h)
// 	ctx.Writer.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
// 	ctx.Writer.Header().Set("Pragma", "no-cache")
// 	ctx.Writer.Header().Set("Expires", "0")

// 	ext := ".png"
// 	var content bytes.Buffer
// 	switch ext {
// 	case ".png":
// 		ctx.Writer.Header().Set("Content-Type", "image/png")
// 		captcha.WriteImage(&content, captchaId, width, height)
// 	case ".wav":
// 		ctx.Writer.Header().Set("Content-Type", "audio/x-wav")
// 		captcha.WriteAudio(&content, captchaId, "zh")
// 	default:
// 		return captcha.ErrNotFound
// 	}

// 	http.ServeContent(ctx.Writer, ctx.Request, captchaId+ext, time.Time{}, bytes.NewReader(content.Bytes()))

// 	return nil
// }

// /**
//  * dchest版验证验证码
//  *
//  * @param *gin.Context ctx -----------------------------------必传
//  * @param string code ----------------------------------------必传
//  * @return bool
//  * @author huwl
//  */
// func VerifyCaptcha(ctx *gin.Context, code string) bool {
// 	session := sessions.Default(ctx)
// 	captchaId := session.Get("captcha")

// 	if captchaId == nil {
// 		return false
// 	}

// 	session.Delete("captcha")
// 	session.Save()
// 	return captcha.VerifyString(captchaId.(string), code)
// }

/**
 * 获取get请求参数且有默认值
 *
 * @param *http.Request r --------------------------必传
 * @param string key -------------------------------必传
 * @param []string defaultValue --------------------非必传
 * @return string
 * @author huwl
 */
func DefaultQuery(r *http.Request, key string, defaultValue ...string) string {
	val := ""
	if len(defaultValue) > 0 {
		val = defaultValue[0]
	}
	value := r.URL.Query().Get(key)
	if value == "" || value == "0" {
		value = val
	}

	return value
}
